/**
 * @details: 
 * @brief: 
 * @version: 1.0
 * @author: liuri
 * @Date: 2021-10-05 05:00:51
 * @LastEditors: liuri
 * @LastEditTime: 2021-10-05 05:13:00
 * @FilePath: /TestProject/cPath/vtysh/cmd_token.c
 * @Copyright (C) 2021 liuri. All rights reserved.
 */

#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
#include "cmd.h"


int keyword_argc;
const char **keyword_argv;

#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION)
#define CMD_ARGC_MAX   25
#define MATCHER_ERROR(matcher_rv) \
  (   (matcher_rv) == MATCHER_INCOMPLETE \
   || (matcher_rv) == MATCHER_NO_MATCH \
   || (matcher_rv) == MATCHER_AMBIGUOUS \
   || (matcher_rv) == MATCHER_EXCEED_ARGC_MAX \
  )
  
// enum filter_type
// {
//     FILTER_RELAXED,
//     FILTER_STRICT
// };


// 用于解析字符串
struct format_parser_state
{
    vector topvect;     /* Top level vector */
    vector intvect;     /* Intermediate level vector, used when there's * a multiple in a keyword. */
    vector curvect;     /* current vector where read tokens should be appended. */

    const char *string; /* 命令行字符串，常量 */
    const char *cp;     /* 命令行字符串 */
    const char *dp;     /* 描述字符串 */
    int in_keyword;     /**< 当前 keyword  数量 */
    int in_multiple;    /**< 当前 multiple 数量 */
    int just_read_word; /* flag to remember if the last thing we red was a
                        * real word and not some abstract token */
};




static void
format_parser_error(struct format_parser_state *state, const char *message)
{
    int offset = state->cp - state->string + 1;

    fprintf(stderr, "\nError parsing command: \"%s\"\n", state->string);
    fprintf(stderr, "                        %*c\n", offset, '^');
    fprintf(stderr, "%s at offset %d.\n", message, offset);
    fprintf(stderr, "This is a programming error. Check your DEFUNs etc.\n");
    exit(1);
}

static char *
format_parser_desc_str(struct format_parser_state *state)
{
    const char *cp, *start;
    char *token;
    int strlen;

    cp = state->dp;

    if (cp == NULL)
        return NULL;

    /* 获取开始位置 */
    while (isspace ((int) *cp) && *cp != '\0')
        cp++;

    /* Return if there is only white spaces */
    if (*cp == '\0')
        return NULL;

    start = cp;

    // 获取结束位置
    while (!(*cp == '\r' || *cp == '\n') && *cp != '\0')
        cp++;

    strlen = cp - start;
    token = malloc (strlen + 1);
    memcpy (token, start, strlen);
    *(token + strlen) = '\0';

    // 指定下一个帮助信息
    state->dp = cp;

    return token;
}

static void
format_parser_read_word(struct format_parser_state *state)
{
    const char *start;
    int len;
    char *cmd;
    struct cmd_token *token;

    start = state->cp;

    // 获取结束的位置
    while (state->cp[0] != '\0'
            && !strchr("\r\n(){}|", state->cp[0]) // 判断 cp[0] 是否在字符串位置
            && !isspace((int)state->cp[0]))
    state->cp++;

    len = state->cp - start;
    cmd = malloc(len + 1);
    memcpy(cmd, start, len);
    cmd[len] = '\0';

    token = malloc(sizeof(*token));
    token->type = TOKEN_TERMINAL;
    if (strcmp (cmd, "A.B.C.D") == 0)
        token->terminal = TERMINAL_IPV4;
    else if (strcmp (cmd, "A.B.C.D/M") == 0)
        token->terminal = TERMINAL_IPV4_PREFIX;
    else if (strcmp (cmd, "X:X::X:X") == 0)
        token->terminal = TERMINAL_IPV6;
    else if (strcmp (cmd, "X:X::X:X/M") == 0)
        token->terminal = TERMINAL_IPV6_PREFIX;
    else if (cmd[0] == '[')
        token->terminal = TERMINAL_OPTION;
    else if (cmd[0] == '.')
        token->terminal = TERMINAL_VARARG;
    else if (cmd[0] == '<')
        token->terminal = TERMINAL_RANGE;
    else if (cmd[0] >= 'A' && cmd[0] <= 'Z')
        token->terminal = TERMINAL_VARIABLE;
    else
        token->terminal = TERMINAL_LITERAL;

    token->cmd = cmd;
    token->desc = format_parser_desc_str(state);
    vector_set(state->curvect, token);

    if (state->in_keyword == 1)
        state->in_keyword = 2;

    state->just_read_word = 1;
}



static void
format_parser_begin_keyword(struct format_parser_state *state)
{
    struct cmd_token *token;
    vector keyword_vect;

    if (state->in_keyword || state->in_multiple)
        format_parser_error(state, "Unexpected '{'");

    state->cp++;
    state->in_keyword = 1;

    token = malloc(sizeof(*token));
    token->type = TOKEN_KEYWORD;
    token->keyword = vector_init(VECTOR_MIN_SIZE);

    keyword_vect = vector_init(VECTOR_MIN_SIZE);
    vector_set(token->keyword, keyword_vect);

    vector_set(state->curvect, token);
    state->curvect = keyword_vect;
}

static void
format_parser_begin_multiple(struct format_parser_state *state)
{
    struct cmd_token *token;

    if (state->in_keyword == 1)
        format_parser_error(state, "Keyword starting with '('");

    if (state->in_multiple)
        format_parser_error(state, "Nested group");

    state->cp++;
    state->in_multiple = 1;
    state->just_read_word = 0;

    token = malloc(sizeof(*token));
    token->type = TOKEN_MULTIPLE;
    token->multiple = vector_init(VECTOR_MIN_SIZE);

    vector_set(state->curvect, token);
    if (state->curvect != state->topvect)
        state->intvect = state->curvect;
    state->curvect = token->multiple;
}

static void
format_parser_end_keyword(struct format_parser_state *state)
{
    if (state->in_multiple
        || !state->in_keyword)
    format_parser_error(state, "Unexpected '}'");

    if (state->in_keyword == 1)
        format_parser_error(state, "Empty keyword group");

    state->cp++;
    state->in_keyword = 0;
    state->curvect = state->topvect;
}


static void
format_parser_end_multiple(struct format_parser_state *state)
{
    char *dummy;

    if (!state->in_multiple)
        format_parser_error(state, "Unexpected ')'");

    if (vector_max(state->curvect) == 0)
        format_parser_error(state, "Empty multiple section");

    if (!state->just_read_word)
    {
        /* There are constructions like
        * 'show ip ospf database ... (self-originate|)'
        * in use.
        * The old parser reads a description string for the
        * word '' between |) which will never match.
        * Simulate this behvaior by dropping the next desc
        * string in such a case. */

        dummy = format_parser_desc_str(state);
        free(dummy);
    }

    state->cp++;
    state->in_multiple = 0;

    if (state->intvect)
        state->curvect = state->intvect;
    else
        state->curvect = state->topvect;
}

static void
format_parser_handle_pipe(struct format_parser_state *state)
{
    struct cmd_token *keyword_token;
    vector keyword_vect;

    if (state->in_multiple)
    {
        state->just_read_word = 0;
        state->cp++;
    }
    else if (state->in_keyword)
    {
        state->in_keyword = 1;
        state->cp++;

        keyword_token = vector_slot(state->topvect,
                                    vector_max(state->topvect) - 1);
        keyword_vect = vector_init(VECTOR_MIN_SIZE);
        vector_set(keyword_token->keyword, keyword_vect);
        state->curvect = keyword_vect;
    }
    else
    {
        format_parser_error(state, "Unexpected '|'");
    }
}


/**
 * @brief: 命令格式和帮助信息解析
 *
 */
vector cmd_parse_format(const char *string, const char *descstr)
{
    struct format_parser_state state;

    if (string == NULL)
        return NULL;

    memset(&state, 0, sizeof(state));
    state.topvect = state.curvect = vector_init(VECTOR_MIN_SIZE);
    state.cp = state.string = string;
    state.dp = descstr;

    while (1)
    {
        while (isspace((int)state.cp[0]) && state.cp[0] != '\0')
            state.cp++;

        switch (state.cp[0])
        {
            case '\0':
                if (state.in_keyword || state.in_multiple)
                    format_parser_error(&state, "Unclosed group/keyword");
                return state.topvect;
            case '{': // 新建一个keyword 向量
                format_parser_begin_keyword(&state);
                break;
            case '(': // 新建一个多选的 multiple 向量
                format_parser_begin_multiple(&state);
                break;
            case '}': // 不能往上一层向量，只能往顶层，
                format_parser_end_keyword(&state);
                break;
            case ')': // 往上一层向量
                format_parser_end_multiple(&state);
                break;
            case '|':
                format_parser_handle_pipe(&state);
                break;
            default: // 在当前向量中添加关键字，cmd
                format_parser_read_word(&state);
        }
    }
}





/**
 * @brief: 命令匹配
 */
// int cmd_match(vector ,const char *string)
// {
//     return 0;
// }

#if 0
int main(int argc, char *args[])
{
    vector vec;
    // p *(struct cmd_token*)vec->index[2]
    // p *(struct cmd_token*) ((struct cmd_token*)vec->index[2])->multiple->index[0]
    // (gdb) p *(struct cmd_token*) ((struct cmd_token*)vec->index[2])->multiple->index[0]
    // $2 = {type = TOKEN_TERMINAL, terminal = TERMINAL_LITERAL, multiple = 0x0, keyword = 0x0, cmd = 0x6042c0 "emergencies", desc = 0x0}
    // (gdb) p *(struct cmd_token*) ((struct cmd_token*)vec->index[2])->multiple->index[1]
    // $3 = {type = TOKEN_TERMINAL, terminal = TERMINAL_LITERAL, multiple = 0x0, keyword = 0x0, cmd = 0x604450 "test", desc = 0x0}
    // (gdb) p *(struct cmd_token*) ((struct cmd_token*)vec->index[2])->multiple->index[2]
    // $4 = {type = TOKEN_TERMINAL, terminal = TERMINAL_LITERAL, multiple = 0x0, keyword = 0x0, cmd = 0x6044a0 "debugging", desc = 0x0}
    vec = cmd_parse_format("log stdout (emergencies test |debugging)",""); // multiple 节点

    // (gdb) p ((vector)0x604610)->index[0]
    // $5 = (void *) 0x604650
    // (gdb) p (struct cmd_token*)((vector)0x604610)->index[0]
    // $6 = (struct cmd_token *) 0x604650
    vec = cmd_parse_format("log stdout {emergencies test |debugging}",""); // keyword 节点, 不支持 

    vec = cmd_parse_format("no log stdout [LEVEL]","");

    //  p *(struct cmd_token*)vec->index[3]
    //  p *(struct cmd_token*) ((struct cmd_token*)vec->index[3])->multiple->index[0]
    vec = cmd_parse_format("ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)",""); // multiple 节点
    
    return 0;
}
#endif